package com.zatech.cgnci.project.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zatech.cgnci.project.base.context.UserContext;
import com.zatech.cgnci.project.base.enums.ClaimCaseStatusEnum;
import com.zatech.cgnci.project.base.enums.DirectionEnum;
import com.zatech.cgnci.project.base.enums.ResponseCodeEnum;
import com.zatech.cgnci.project.base.exception.GlobalException;
import com.zatech.cgnci.project.base.utils.BeanUtils;
import com.zatech.cgnci.project.base.utils.DateUtils;
import com.zatech.cgnci.project.base.utils.VerifyUtils;
import com.zatech.cgnci.project.dto.ReportCreateDTO;
import com.zatech.cgnci.project.entity.CgnciClaimsInventoryDO;
import com.zatech.cgnci.project.entity.CgnciClaimsInventoryDetailDO;
import com.zatech.cgnci.project.entity.CgnciOutstandingClaimsAnnualSummaryDO;
import com.zatech.cgnci.project.entity.UserEntity;
import com.zatech.cgnci.project.mapper.*;
import com.zatech.cgnci.project.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zatech.cgnci.project.vo.*;
import io.swagger.annotations.ApiModelProperty;
import org.apache.catalina.User;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 赔案清单报表 服务实现类
 * </p>
 *
 * @author Generator
 * @since 2025-02-20
 */
@Service
public class CgnciClaimsInventoryServiceImpl extends ServiceImpl<CgnciClaimsInventoryMapper, CgnciClaimsInventoryDO> implements CgnciClaimsInventoryService {
    @Resource
    private CgnciClaimCaseMapper claimCaseService;
    @Resource
    private CgnciDirectInsuranceService cgnciDirectInsuranceService;
    @Resource
    private CgnciReinsuranceInfoService cgnciReinsuranceInfoService;
    @Resource
    private CgnciBusinessInsuranceService businessInsuranceService;
    @Resource
    private CgnciClaimReserveService claimReserveService;
    @Resource
    private CgnciProportionalLiabilityInvoiceMapper lLiabilityInvoiceMapper;
    @Resource
    private CgnciClaimsInventoryDetailService inventoryDetailService;
    @Resource
    private CgnciClaimsInventoryDetailMapper detailMapper;

    @Override
    public void createClaimsInventory(ReportCreateDTO extractDTO) {
        UserEntity user = UserContext.getUser();
        List<ClaimsInventoryVO> claimsInventoryVOS = claimCaseService.queryInventory(extractDTO.getInitialDate(),extractDTO.getPeriodDate());
        List<CgnciClaimsInventoryDO> inventoryDOS = new ArrayList<>();
        claimsInventoryVOS.forEach(vos->{
            CgnciClaimsInventoryDO inventoryDO = new CgnciClaimsInventoryDO();
            inventoryDO.setId(BeanUtils.getUUID());
            inventoryDO.setRegisterId(extractDTO.getRegisterId());
            inventoryDO.setPolicyNo(vos.getPolicyNo());
            inventoryDO.setRiskSubType(vos.getRiskSubType());
            inventoryDO.setPolicyYear(vos.getPolicyYear());
            inventoryDO.setCaseNo(vos.getCaseNo());
            inventoryDO.setCaseName(vos.getCaseName());
            inventoryDO.setAccidentDate(vos.getAccidentDate());
            inventoryDO.setNotifyDate(vos.getNotifyDate());
            inventoryDO.setReportAmount(vos.getReportAmount());
            inventoryDO.setSettlementCurrency(vos.getSettlementCurrency());
            inventoryDO.setLiabilityRatio(calculateRatio(vos.getInsuranceId()));
            inventoryDO.setCaseStatus(ClaimCaseStatusEnum.getEnumByCode(vos.getCaseStatus()).getDescr());
            inventoryDO.setLatestCloseAmount(vos.getLatestCloseAmount());
            inventoryDO.setCloseTime(vos.getCloseTime());
            inventoryDO.setCreator(user.getNameAndCode());
            inventoryDO.setCreateTime(new Date());
            calculateReserve(inventoryDO,vos,extractDTO);
            calculateInventoryDetail(inventoryDO,vos.getInsuranceId());
            inventoryDOS.add(inventoryDO);
        });
    }

    private void calculateInventoryDetail(CgnciClaimsInventoryDO inventoryDO,Long insuranceId) {
        // 分入信息
        //CgnciReinsuranceInfoVO inReinsuranceInfoVO = cgnciReinsuranceInfoService.getDivideInReinsuranceByInsuranceId(insuranceId);
        List<ProportionalLiabilityInvoiceVO> liabilityInvoiceVOS = lLiabilityInvoiceMapper.getInvoiceDetail(inventoryDO.getPolicyNo());
        List<CgnciClaimsInventoryDetailDO> detailDOS = new ArrayList<>();
        liabilityInvoiceVOS.forEach(inDO->{
            CgnciClaimsInventoryDetailDO detailDO = new CgnciClaimsInventoryDetailDO();
            detailDO.setClaimsId(inventoryDO.getId());
            detailDO.setDirection(inDO.getDirection());
            if(DirectionEnum.PAYMENT.getCode().equals(inDO.getDirection())){
                detailDO.setPaymentCompany(inDO.getCedingCompany());
            }else{
                detailDO.setPaymentCompany(inDO.getReceiver());
            }
            detailDO.setSettledClaimAmount(inDO.getAmount());
            detailDO.setUpmNo(inDO.getUpmNumber());
            detailDO.setSettledPaymentTime(inDO.getPostingDate());
            detailDO.setCreateTime(new Date());
            detailDOS.add(detailDO);
        });
       if(CollectionUtils.isNotEmpty(detailDOS)){
           inventoryDetailService.saveBatch(detailDOS);
       }
        // 临分分出信息
        //CgnciReinsuranceInfoVO outReinsurance =cgnciReinsuranceInfoService.getLfDivideOutReinsuranceByInsuranceId(insuranceId);

    }

    private void calculateReserve(CgnciClaimsInventoryDO inventoryDO, ClaimsInventoryVO vos,ReportCreateDTO extractDTO) {
       String periodDnd=DateUtils.getEndMonth(extractDTO.getValuationYear()+"/"+extractDTO.getValuationMonth());
        //查询理赔赔案准备金历史金额在期末日期之前
        inventoryDO.setValuationReserve(claimReserveService.reserveCollect(vos.getCaseNo(),DateUtils.parseDate(periodDnd,DateUtils.FORMAT_8),null));
        inventoryDO.setProvisionReserve(claimReserveService.reserveCollect(vos.getCaseNo(),DateUtils.parseDate(periodDnd,DateUtils.FORMAT_8),"YES"));
        CgnciClaimReserveVO reserveVO =  claimReserveService.detail(vos.getCaseNo());
        if(Objects.nonNull(reserveVO)&&CollectionUtils.isNotEmpty(reserveVO.getDetails())){
            inventoryDO.setReleaseReserve(BeanUtils.sumBigDecimalProperty(claimReserveService.detail(vos.getCaseNo()).getDetails(), CgnciClaimReserveVO.ReserveDetailVO::getApprovedReserve));
        }else{
            inventoryDO.setReleaseReserve(new BigDecimal(0));
        }
        inventoryDO.setValuationRecovery(inventoryDO.getValuationReserve());
        inventoryDO.setProvisionRecovery(inventoryDO.getProvisionReserve());
        inventoryDO.setReleaseRecovery(inventoryDO.getReleaseReserve());
    }

    private BigDecimal calculateRatio(Long insuranceId) {
        // 净自留份额
       return businessInsuranceService.getRetentionAmount(cgnciReinsuranceInfoService.getDivideInReinsuranceByInsuranceId(insuranceId),cgnciDirectInsuranceService.getDirectInsuranceByBusinessId(insuranceId));
    }

    @Override
    public ExcelWriter exportClaimsInventory(Long registerId) {
        List<CgnciClaimsInventoryDO> claimsInventoryDOList =this.baseMapper.selectList(new LambdaQueryWrapper<CgnciClaimsInventoryDO>()
                .eq(CgnciClaimsInventoryDO::getRegisterId, registerId));
        ExcelWriter writer = ExcelUtil.getWriter(true);
        try {
            List<String> title = CollUtil.newArrayList(
                    "保单号",
                    "险种",
                    "承保年",
                    "案件编号",
                    "案件",
                    "出险时间",
                    "通知时间",
                    "原赔款总金额",
                    "自保公司摊赔比例",
                    "币种",
                    "评估期末再保前准备金余额",
                    "计提再保前准备金总额",
                    "释放再保前准备金金额",
                    "评估期末再保摊回准备金金额",
                    "再保摊回的准备金总额",
                    "释放再保摊回准备金金额",
                    "分入人",
                    "已决赔款金额",
                    "支付UPM号 ",
                    "已决赔款支付时间",
                    "分出人",
                    "再保摊回赔款金额",
                    "实收UPM号 ",
                    "再保摊回赔款收取时间",
                    "案件状态",
                    "结案金额",
                    "结案时间");
            writer.writeHeadRow(title);
            CellStyle style = writer.createCellStyle();
            // 设置单元格内文字的对齐方式为居中
            style.setAlignment(HorizontalAlignment.CENTER); // 水平居中
            style.setVerticalAlignment(VerticalAlignment.CENTER); // 垂直居中
            style.setBorderTop(BorderStyle.THIN);
            style.setBorderLeft(BorderStyle.THIN);
            style.setBorderRight(BorderStyle.THIN);
            int initial =1;
            for (CgnciClaimsInventoryDO inventoryDO : claimsInventoryDOList) {
               List<CgnciClaimsInventoryDetailDO>detailDOS =  detailMapper.selectList(new LambdaQueryWrapper<CgnciClaimsInventoryDetailDO>()
                        .eq(CgnciClaimsInventoryDetailDO::getClaimsId,inventoryDO.getId()));
                Map<String, List<CgnciClaimsInventoryDetailDO>> groupedByDirection = detailDOS.stream()
                        .collect(Collectors.groupingBy(CgnciClaimsInventoryDetailDO::getDirection));
                List<CgnciClaimsInventoryDetailDO> outDirectionS = groupedByDirection.get(DirectionEnum.PAYMENT.getCode());
                List<CgnciClaimsInventoryDetailDO> inDirectionS = groupedByDirection.get(DirectionEnum.COLLECTION.getCode());
                int max = Math.max(outDirectionS.size(), inDirectionS.size());
               for(int i= 0;i<max;i++){
                   CgnciClaimsInventoryDetailDO outDetail = new CgnciClaimsInventoryDetailDO();
                   CgnciClaimsInventoryDetailDO inDetail = new CgnciClaimsInventoryDetailDO();
                   if(i<outDirectionS.size()){
                       outDetail = outDirectionS.get(i);
                   }
                   if(i<inDirectionS.size()){
                       inDetail = inDirectionS.get(i);
                   }
                   writer.writeRow(Arrays.asList(
                           inventoryDO.getPolicyNo(),
                           inventoryDO.getRiskSubType(),
                           inventoryDO.getPolicyYear(),
                           inventoryDO.getCaseNo(),
                           inventoryDO.getCaseName(),
                           inventoryDO.getAccidentDate(),
                           inventoryDO.getNotifyDate(),
                           inventoryDO.getReportAmount(),
                           inventoryDO.getLiabilityRatio().multiply(new BigDecimal(100))+"%",
                           inventoryDO.getSettlementCurrency(),
                           inventoryDO.getValuationReserve(),
                           inventoryDO.getProvisionReserve(),
                           inventoryDO.getReleaseReserve(),
                           inventoryDO.getValuationRecovery(),
                           inventoryDO.getProvisionRecovery(),
                           inventoryDO.getReleaseRecovery(),
                           Objects.nonNull(outDetail)?outDetail.getPaymentCompany():null,
                           Objects.nonNull(outDetail)?outDetail.getSettledClaimAmount():null,
                           Objects.nonNull(outDetail)?outDetail.getUpmNo():null,
                           Objects.nonNull(outDetail)?DateUtils.formatDate(outDetail.getSettledPaymentTime(),DateUtils.FORMAT_8):null,
                           Objects.nonNull(inDetail)?inDetail.getPaymentCompany():null,
                           Objects.nonNull(inDetail)?inDetail.getSettledClaimAmount():null,
                           Objects.nonNull(inDetail)?inDetail.getUpmNo():null,
                           Objects.nonNull(inDetail)?DateUtils.formatDate(inDetail.getSettledPaymentTime(),DateUtils.FORMAT_8):null,
                           inventoryDO.getCaseStatus(),
                           inventoryDO.getLatestCloseAmount(),
                           inventoryDO.getCloseTime()
                   ));
               }
                mergeExcel(writer,style,max,initial);
                initial = max+1;
            }
        } catch (Exception e) {
            log.error("导出赔案清单失败", e);
            throw new GlobalException(ResponseCodeEnum.PARAMS_ILLEGAL, "导出赔案清单失败");
        }
        return writer;
    }

    private void mergeExcel(ExcelWriter writer, CellStyle style,int max,int initial) {
        int startCol = 0; // 起始列
        int endCol = 15;  // 结束列
        for (int col = startCol; col <= endCol; col++) {
            writer.merge(initial, max, col, col, null, style);
        }
        startCol = 24; // 起始列
        endCol = 26;  // 结束列
        for (int col = startCol; col <= endCol; col++) {
            writer.merge(initial, max, col, col, null, style);
        }
//        startCol = 10; // 起始列
//        endCol = 26;  // 结束列
//        for (int col = startCol; col <= endCol; col++) {
//            writer.merge(3, 4, col, col, null, style);
//        }
//        writer.merge(1,1,10,13,"保单获取成本",style);
//        writer.merge(1,1,14,18,"期初",style);
//        writer.merge(1,1,19,23,"期末",style);
//        writer.merge(1,1,24,26,"未到期责任准备金",style);
        //writer.merge(2,3,28,33,null,style);

    }
}
